
#include <Archaism/Time.h>
#include <Archaism/String.h>
#include <limits>

_BeginNamespace(Radix)
/*
============================================================================================
==== TimeSpan
============================================================================================
*/
const longlong TimeSpan::kUsecsPerMsec = 1000;
const longlong TimeSpan::kUsecsPerSecond = 1000000;
const longlong TimeSpan::kUsecsPerMinute = 60000000;
const longlong TimeSpan::kUsecsPerHour = 3600000000;
const longlong TimeSpan::kUsecsPerDay = 86400000000;

TimeSpan TimeSpan::maxValue()
{
	return TimeSpan(LLONG_MAX);
}

TimeSpan TimeSpan::minValue()
{
	return TimeSpan(LLONG_MIN);
}

TimeSpan::TimeSpan(longlong usecs)
	:mSpan(usecs)
{}

TimeSpan::TimeSpan(int days, int hours, int mins, int secs, int msecs, int usecs)
	:mSpan(kUsecsPerDay*days + kUsecsPerHour*hours + kUsecsPerMinute*mins + kUsecsPerSecond*secs + kUsecsPerMsec*msecs + usecs)
{}

TimeSpan::TimeSpan(const TimeSpan& other)
	:mSpan(other.mSpan)
{}
	
TimeSpan::~TimeSpan()
{}

TimeSpan& TimeSpan::operator=(const TimeSpan& other)
{
	mSpan = other.mSpan;
	return *this;
}

TimeSpan TimeSpan::operator+(const TimeSpan& other)
{
	return TimeSpan(mSpan + other.mSpan);
}

TimeSpan TimeSpan::operator-(const TimeSpan& other)
{
	return TimeSpan(mSpan - other.mSpan);
}

TimeSpan& TimeSpan::operator+=(const TimeSpan& other)
{
	mSpan += other.mSpan;
	return *this;
}

TimeSpan& TimeSpan::operator-=(const TimeSpan& other)
{
	mSpan -= other.mSpan;
	return *this;
}

bool TimeSpan::operator>(const TimeSpan& other)
{
	return mSpan > other.mSpan;
}

bool TimeSpan::operator<(const TimeSpan& other)
{
	return mSpan < other.mSpan;
}

bool TimeSpan::operator>=(const TimeSpan& other)
{
	return mSpan >= other.mSpan;
}

bool TimeSpan::operator<=(const TimeSpan& other)
{
	return mSpan <= other.mSpan;
}

bool TimeSpan::operator==(const TimeSpan& other)
{
	return mSpan == other.mSpan;
}

bool TimeSpan::operator!=(const TimeSpan& other)
{
	return mSpan != other.mSpan;
}

longlong TimeSpan::dayPart() const
{
	return mSpan / kUsecsPerDay;
}

longlong TimeSpan::hourPart() const
{
	return mSpan % kUsecsPerDay / kUsecsPerHour;
}

longlong TimeSpan::minutePart() const
{
	return mSpan % kUsecsPerDay % kUsecsPerHour / kUsecsPerMinute;
}

longlong TimeSpan::secondPart() const
{
	return mSpan % kUsecsPerDay % kUsecsPerHour % kUsecsPerMinute / kUsecsPerSecond;
}

longlong TimeSpan::millisecondPart() const
{
	return mSpan % kUsecsPerDay % kUsecsPerHour % kUsecsPerMinute % kUsecsPerSecond / kUsecsPerMsec;
}

longlong TimeSpan::microsecondPart() const
{
	return mSpan % kUsecsPerMsec;
}

longlong TimeSpan::totalDays() const
{
	longlong result = mSpan / kUsecsPerDay;
	if(mSpan % kUsecsPerDay > 0)
	{
		result += 1;
	}
	return result;
}

longlong TimeSpan::totalHours() const
{
	longlong result = mSpan / kUsecsPerHour;
	if(mSpan % kUsecsPerHour > 0)
	{
		result += 1;
	}
	return result;
}

longlong TimeSpan::totalMinutes() const
{
	longlong result = mSpan / kUsecsPerMinute;
	if(mSpan % kUsecsPerMinute > 0)
	{
		result += 1;
	}
	return result;
}

longlong TimeSpan::totalSeconds() const
{
	longlong result = mSpan / kUsecsPerSecond;
	if(mSpan % kUsecsPerSecond > 0)
	{
		result += 1;
	}
	return result;
}

longlong TimeSpan::totalMilliseconds() const
{
	longlong result = mSpan / kUsecsPerMsec;
	if(mSpan % kUsecsPerMsec > 0)
	{
		result += 1;
	}
	return result;
}

double TimeSpan::exactDays() const
{
	double result = mSpan / (kUsecsPerDay + 0.0);
	return result;
}

double TimeSpan::exactHours() const
{
	double result = mSpan / (kUsecsPerHour + 0.0);
	return result;
}

double TimeSpan::exactMinutes() const
{
	double result = mSpan / (kUsecsPerMinute + 0.0);
	return result;
}

double TimeSpan::exactSeconds() const
{
	double result = mSpan / (kUsecsPerSecond + 0.0);
	return result;
}

double TimeSpan::exactMilliseconds() const
{
	double result = mSpan / (kUsecsPerMsec + 0.0);
	return result;
}

/*
============================================================================================
==== DateTime
============================================================================================
*/
DateTime::DateTime()
	:mCalendarTime(time(nullptr))
{
	mBrokenTime = localtime(&mCalendarTime);
}

DateTime::DateTime(time_t calendarTime)
	:mCalendarTime(calendarTime)
{
	mBrokenTime = localtime(&mCalendarTime);
}

DateTime::DateTime(int year, int month, int date, int hour, int minute, int sec)
{
	tm t;
	t.tm_year = year-1900;
	t.tm_mon = month-1;
	t.tm_mday = date;
	t.tm_hour = hour;
	t.tm_min = minute;
	t.tm_sec = sec;
	mCalendarTime = mktime(&t);
	mBrokenTime = localtime(&mCalendarTime);
}

DateTime::DateTime(const DateTime& other)
	:mCalendarTime(other.mCalendarTime)
{
	mBrokenTime = localtime(&mCalendarTime);
}

DateTime::~DateTime()
{}

DateTime& DateTime::operator=(const DateTime& other)
{
	mCalendarTime = other.mCalendarTime;
	mBrokenTime = localtime(&mCalendarTime);
	return *this;
}

DateTime DateTime::operator+(const TimeSpan& span)
{
	return DateTime(mCalendarTime + span.totalSeconds());
}

DateTime DateTime::operator-(const TimeSpan& span)
{
	return DateTime(mCalendarTime - span.totalSeconds());
}

DateTime& DateTime::operator+=(const TimeSpan& span)
{
	mCalendarTime += span.totalSeconds();
	mBrokenTime = localtime(&mCalendarTime);
	return *this;
}

DateTime& DateTime::operator-=(const TimeSpan& span)
{
	mCalendarTime -= span.totalSeconds();
	mBrokenTime = localtime(&mCalendarTime);
	return *this;
}

TimeSpan DateTime::operator-(const DateTime& other)
{
	return TimeSpan((mCalendarTime - other.mCalendarTime)*TimeSpan::kUsecsPerSecond);
}

bool DateTime::operator==(const DateTime& other)
{
	return mCalendarTime == other.mCalendarTime;
}

bool DateTime::operator!=(const DateTime& other)
{
	return mCalendarTime != other.mCalendarTime;
}

bool DateTime::operator>(const DateTime& other)
{
	return mCalendarTime > other.mCalendarTime;
}

bool DateTime::operator<(const DateTime& other)
{
	return mCalendarTime < other.mCalendarTime;
}

bool DateTime::operator>=(const DateTime& other)
{
	return mCalendarTime >= other.mCalendarTime;
}

bool DateTime::operator<=(const DateTime& other)
{
	return mCalendarTime <= other.mCalendarTime;
}

time_t DateTime::calendarTime() const
{
	return mCalendarTime;
}

int DateTime::year() const
{
	return mBrokenTime->tm_year + 1900;
}

int DateTime::month() const
{
	return mBrokenTime->tm_mon + 1;
}

int DateTime::date() const
{
	return mBrokenTime->tm_mday;
}

int DateTime::hour() const
{
	return mBrokenTime->tm_hour;
}

int DateTime::minute() const
{
	return mBrokenTime->tm_min;
}

int DateTime::second() const
{
	return mBrokenTime->tm_sec;
}

int DateTime::dayOfWeek() const
{
	return mBrokenTime->tm_wday;
}

int DateTime::dayofYear() const
{
	return mBrokenTime->tm_yday;
}

bool DateTime::isLeapYear() const
{
	bool result = false;
	int year = this->year();
	if((year % 4 == 0) || (year % 400 == 0))
	{
		result = true;
	}
	return result;
}

String DateTime::toString(const String& fmt)
{
	String result = fmt;

	result = StringUtil::replace(result, _T("YYYY"), StringUtil::format(_T("%04d"), this->year()));
	result = StringUtil::replace(result, _T("YYY"),  StringUtil::format(_T("%03d"), this->year()%1000));
	result = StringUtil::replace(result, _T("YY"),   StringUtil::format(_T("%02d"), this->year()%100));
	result = StringUtil::replace(result, _T("Y"),    StringUtil::format(_T("%d"),   this->year()));
	
	result = StringUtil::replace(result, _T("MM"),   StringUtil::format(_T("%02d"), this->month()));
	result = StringUtil::replace(result, _T("M"),    StringUtil::format(_T("%d"),   this->month()));

	result = StringUtil::replace(result, _T("DD"),   StringUtil::format(_T("%02d"), this->date()));
	result = StringUtil::replace(result, _T("D"),    StringUtil::format(_T("%d"),   this->date()));

	result = StringUtil::replace(result, _T("hh"),   StringUtil::format(_T("%02d"), this->hour()));
	result = StringUtil::replace(result, _T("h"),    StringUtil::format(_T("%d"),   this->hour()));

	result = StringUtil::replace(result, _T("mm"),   StringUtil::format(_T("%02d"), this->minute()));
	result = StringUtil::replace(result, _T("m"),    StringUtil::format(_T("%d"),   this->minute()));

	result = StringUtil::replace(result, _T("ss"),   StringUtil::format(_T("%02d"), this->second()));
	result = StringUtil::replace(result, _T("s"),    StringUtil::format(_T("%d"),   this->second()));
	
	return result;
}

_EndNamespace(Radix)
